/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.harmony.javax.naming.spi;

import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;

import org.apache.harmony.javax.naming.CannotProceedException;
import org.apache.harmony.javax.naming.Context;
import org.apache.harmony.javax.naming.Name;
import org.apache.harmony.javax.naming.NamingException;
import org.apache.harmony.javax.naming.NoInitialContextException;
import org.apache.harmony.javax.naming.RefAddr;
import org.apache.harmony.javax.naming.Reference;
import org.apache.harmony.javax.naming.Referenceable;
import org.apache.harmony.javax.naming.StringRefAddr;
import org.apache.harmony.jndi.internal.EnvironmentReader;
import org.apache.harmony.jndi.internal.UrlParser;
import org.apache.harmony.jndi.internal.nls.Messages;

/**
 * The <code>NamingManager</code> class should not be instantiated although it
 * can be extended by classes within the <code>javax.naming.spi</code> package -
 * see {@link DirectoryManager}. All its methods are static.
 * <p>
 * The methods are used by service providers for accessing object and state
 * factories and for determining continuation contexts. Many of the methods
 * create objects. These may be <code>Context</code> objects or objects referred
 * to by the naming service.
 * </p>
 * <p>
 * The <code>Name</code> and <code>Hashtable</code> arguments passed to the
 * <code>NamingManager</code> methods remain owned purely by the calling method.
 * They must not be changed or referenced.
 * </p>
 * <p>
 * It should be noted that it is possible for an application to access a
 * namespace other than that supplied by the default <code>InitialContext</code>
 * (as specified by <code>Context.INITIAL_CONTEXT_FACTORY</code>). It is
 * possible to call the following <code>InitialContext</code> methods passing a
 * URL string either as the <code>String</code> or <code>Name</code> parameter:
 * <code>lookup, bin, rebind, unbind, rename, list, listBindings, 
 * destroySubcontext, createSubcontext, lookupLink, getNameParser</code>. This
 * allows you to have one <code>InitialContext</code> object where these methods
 * usually use the default initial context but access a URL <code>Context</code>
 * instead when invoked with a URL string.
 * </p>
 * <p>
 * A URL string is of the format abc:\nnnnnn where abc is the scheme of the URL.
 * (See <code>InitialContext</code> where it refers to RFC1738.) When a URL
 * string is supplied to those <code>InitialContext</code> methods, a URL
 * context is used instead of the default initial context when performing that
 * method. URL context factories are used to create URL contexts. A URL context
 * factory is really just a service provider's implementation of an
 * <code>ObjectFactory</code>. It is not essential that a service provider
 * supplies one if they do not wish to support URL <code>Contexts.</code>
 * </p>
 * <p>
 * See the <code>getURLContext</code> method for a description of how a URL
 * context factory is located.
 * </p>
 * <p>
 * Please note that multithreaded access to this class must be safe. For
 * example, for thread safety, it should not be possible for one thread to read
 * the installed <code>InitialContextFactoryBuilder</code> or
 * <code>ObjectFactoryBuilder</code> while another thread is in the process of
 * setting it.
 * </p>
 * <p>
 * Also note that privileges should be granted to get the context classloader
 * and to read the resource files.
 * </p>
 * 
 * @see DirectoryManager
 */
public class NamingManager {

	/**
	 * The property name of <code>CannotProceedException</code> in a context's
	 * environment.
	 */
	public static final String CPE = "java.naming.spi.CannotProceedException"; //$NON-NLS-1$

	static InitialContextFactoryBuilder icfb;

	static ObjectFactoryBuilder ofb;

	private static Class<?> classForName(final String className)
			throws ClassNotFoundException {

		final Class<?> cls = AccessController
				.doPrivileged(new PrivilegedAction<Class<?>>() {
					@Override
					public Class<?> run() {
						// try thread context class loader first
						try {
							return Class.forName(className, true, Thread
									.currentThread().getContextClassLoader());
						} catch (final ClassNotFoundException e) {
							// Ignored.
						}
						// try system class loader second
						try {
							return Class.forName(className, true,
									ClassLoader.getSystemClassLoader());
						} catch (final ClassNotFoundException e1) {
							// Ignored.
						}
						// return null, if fail to load class
						return null;
					}
				});

		if (cls == null) {
			// jndi.1C=class {0} not found
			throw new ClassNotFoundException(Messages.getString(
					"jndi.1C", className)); //$NON-NLS-1$
		}

		return cls;
	}

	/**
	 * Create the next context when using federation. All the information
	 * required to do this is contained in the
	 * <code>CannotProceedException</code> <code>e</code>. If the resolved
	 * object is null then throw the supplied
	 * <code>CannotProceedException</code> <code>e</code> using the stack
	 * details from this thread. The resolved object in <code>e</code> may
	 * already be a <code>Context</code>. This is the case where the service
	 * provider gives an explicit pointer to the next naming system. A Context
	 * object is returned as the continuation context, but need not be the same
	 * object instance as the resolved object.
	 * <p>
	 * If the resolved object is not already a <code>Context</code> then it is
	 * necessary to use the resolved object together with the
	 * <code>altName</code> name, the <code>altNameCtx</code> context and the
	 * environment hashtable to get an instance of the object. This should then
	 * be a context which is returned as the continuation context. If an
	 * instance cannot be obtained then throw the supplied
	 * <code>CannotProceedException</code> using the stack details from this
	 * thread.
	 * </p>
	 * <p>
	 * This method is responsible for setting the property denoted by the
	 * <code>CPE</code> string to be the supplied
	 * <code>CannotProceedException</code> for the exception <code>e</code>
	 * environment. The continuation context should then inherit this property.
	 * </p>
	 * 
	 * @param cpe
	 *            the <code>CannotProceedException</code> generated by the
	 *            context of the previous naming system when it can proceed no
	 *            further.
	 * @return the next Context when using federation
	 * @throws NamingException
	 *             if the resolved object is null or if a context cannot be
	 *             obtained from it either directly or indirectly.
	 */
	@SuppressWarnings("unchecked")
	public static Context getContinuationContext(CannotProceedException cpe)
			throws NamingException {

		Context ctx = null;

		// set CPE property of the env
		if (cpe.getEnvironment() == null) {
			cpe.setEnvironment(new Hashtable<String, CannotProceedException>());
		}
		((Hashtable<String, CannotProceedException>) cpe.getEnvironment()).put(
				CPE, cpe);

		// if resolved object is null
		if (null == cpe.getResolvedObj()) {
			// re-throw cpe
			cpe.fillInStackTrace();
			throw cpe;
		}

		// if cpe's resolved obj is Context
		if (cpe.getResolvedObj() instanceof Context) {
			// accept it as the continuation context
			ctx = (Context) cpe.getResolvedObj();
		} else {
			// otherwise, call getObjectInstance() to get a context instance
			try {
				ctx = (Context) getObjectInstance(cpe.getResolvedObj(),
						cpe.getAltName(), cpe.getAltNameCtx(),
						cpe.getEnvironment());
			} catch (final Exception ex) {
				// throw back CPE in case of any exception
				throw cpe;
			}
			// if ctx cannot be obtained
			if (null == ctx) {
				// re-throw CPE
				cpe.fillInStackTrace();
				throw cpe;
			}
		}

		// return the continuation context
		return ctx;
	}

	/**
	 * Create an <code>InitialContext</code> from either a previously installed
	 * <code>InitialContextFactoryBuilder</code> or from the
	 * <code>Context.INITIAL_CONTEXT_FACTORY</code> property in the supplied
	 * <code> Hashtable h</code> if no builder is installed. An installed
	 * <code>InitialContextFactoryBuilder</code> can generate a factory which
	 * can be used to create the <code>InitialContext</code>. The
	 * <code>Context.INITIAL_CONTEXT_FACTORY</code> property contains the class
	 * of a factory which can be used to create the <code>InitialContext</code>.
	 * 
	 * @param h
	 *            a hashtable containing properties and values - may be null
	 * @return an <code>InitialContext</code>
	 * @throws NoInitialContextException
	 *             if the <code>InitialContext</code> cannot be created.
	 * @throws NamingException
	 */
	public static Context getInitialContext(Hashtable<?, ?> h)
			throws NoInitialContextException, NamingException {

		// if InitialContextFactoryBuilder is set
		if (null != icfb) {
			// create InitialContext using builder
			return icfb.createInitialContextFactory(h).getInitialContext(h);
		}

		// create InitialContext using factory specified in hashtable
		try {
			// get factory class name
			final String factoryClassName = (String) h
					.get(Context.INITIAL_CONTEXT_FACTORY);
			// new factory instance
			final Class<?> factoryClass = classForName(factoryClassName);
			final InitialContextFactory factory = (InitialContextFactory) factoryClass
					.newInstance();
			// create initial context instance using the factory
			return factory.getInitialContext(h);
		} catch (final NamingException e) {
			// throw NamingException
			throw e;
		} catch (final Exception e) {
			// failed, throw NoInitialContextException
			// jndi.20=Failed to create InitialContext using factory specified
			// in hashtable {0}
			final NamingException nex = new NoInitialContextException(
					Messages.getString("jndi.20", h)); //$NON-NLS-1$
			nex.setRootCause(e);
			throw nex;
		}
	}

	/**
	 * Create an object from either a previously installed
	 * <code>ObjectFactoryBuilder</code> or from a supplied reference or from
	 * the <code>Context.OBJECT_FACTORIES</code> property in the supplied
	 * <code>Hashtable h</code>.
	 * <p>
	 * An installed <code>ObjectFactoryBuilder</code> can generate a factory
	 * which can be used to create the object instance to return to caller. Any
	 * encountered exceptions are thrown.
	 * </p>
	 * <p>
	 * If an <code>ObjectFactoryBuilder</code> has not been installed then the
	 * supplied <code>Object o</code> may provide a <code>Reference</code> or
	 * <code>Referenceable</code> object. If so, then that <code>Object o</code>
	 * may have an associated class in a factory which could be loaded and used
	 * to create the object instance. If the factory class cannot be loaded then
	 * the <code>URLClassLoader</code> may be able to load a class from the list
	 * of URLs specified in the reference's factory class location. Any
	 * exceptions encountered are passed up.
	 * </p>
	 * <p>
	 * If a reference is supplied but no factory class can be loaded from it
	 * then this method returns the supplied object <code>o</code>.
	 * </p>
	 * <p>
	 * If a factory class loads successfully and can then be used to create an
	 * object instance then that instance is returned to the caller.
	 * </p>
	 * <p>
	 * If no factory name was associated with the <code>Reference</code> object
	 * <code>o</code> then see whether the <code>Reference</code> or
	 * <code>Referenceable</code> object has any <code>StringRefAddrs</code> of
	 * address type URL or url in its address list. For each entry in the list,
	 * in the order they appear in the list, it may be possible to use the URL
	 * factory to create the object. A URL in a <code>StringRefAddr</code>
	 * should have a scheme which can be used to locate the associated URL
	 * context factory in the same way as in the <code>getURLContext</code>
	 * method. (The scheme is the part which comes before :\. For example the
	 * URL http://www.apache.org has the scheme http.) A URL with no scheme
	 * would be ignored for these purposes.
	 * </p>
	 * <p>
	 * If no <code>ObjectFactoryBuilder</code> was installed, no factory class
	 * name is supplied with a <code>Reference</code> and no URL contexts
	 * succeeded in creating an <code>Object</code> then try the factories in
	 * <code>Context.OBJECT_FACTORIES</code> for this environment. Also try the
	 * provider resource file belonging to the context <code>c</code>. (See
	 * <code>Context</code> description for details of Provider resource files.)
	 * If any factory throws an exception then pass that back to the caller - no
	 * further factories are tried.
	 * </p>
	 * <p>
	 * If all factories fail to load or create the <code>Object</code> then
	 * return the argument object <code>o</code> as the returned object.
	 * </p>
	 * 
	 * @param o
	 *            an object which may provide reference or location information.
	 *            May be null.
	 * @param n
	 *            The name of the <code>Object</code> relative to the default
	 *            initial context(or relative to the Context c if it is
	 *            supplied)
	 * @param c
	 *            the <code>Context</code> to which the <code>Name</code> is
	 *            relative
	 * @param h
	 *            a <code>Hashtable</code> containing environment properties and
	 *            values - may be null
	 * @return a new <code>Object</code> or the supplied <code>Object o</code>
	 *         if one cannot be created.
	 * @throws NamingException
	 *             if one is encountered
	 * @throws Exception
	 *             if any other exception is encountered
	 */
	public static Object getObjectInstance(Object o, Name n, Context c,
			Hashtable<?, ?> h) throws NamingException, Exception {

		// 1. try ObjectFactoryBuilder, if it is set
		if (null != ofb) {
			// use the builder to create an object factory
			final ObjectFactory factory = ofb.createObjectFactory(o, h);
			// get object instance using the factory and return
			return factory.getObjectInstance(o, n, c, h);
		}

		// 2. see whether o is a Referenceable or a Reference
		Reference ref = null;
		if (o instanceof Referenceable) {
			ref = ((Referenceable) o).getReference();
		}
		if (o instanceof Reference) {
			ref = (Reference) o;
		}
		// if o is a Referenceable or a Reference
		if (null != ref) {
			// if a factory class name is supplied by the reference, use it to
			// create
			if (null != ref.getFactoryClassName()) {
				return getObjectInstanceByFactoryInReference(ref, o, n, c, h);
			}
			// see if ref has any StringRefAddrs of address type URL,
			final Object result = getObjectInstanceByUrlRefAddr(n, c, h, ref);
			// if success, return it
			if (null != result) {
				return result;
			}
		}

		// 3. try Context.OBJECT_FACTORIES
		final Object result = getObjectInstanceByObjectFactory(o, n, c, h);
		if (null != result) {
			return result;
		}

		// all failed, just return o
		return o;
	}

	private static Object getObjectInstanceByFactoryInReference(Reference ref,
			Object o, Name n, Context c, Hashtable<?, ?> h) throws Exception {
		ObjectFactory factory = null;

		// try load the factory by its class name
		try {
			factory = (ObjectFactory) classForName(ref.getFactoryClassName())
					.newInstance();
		} catch (final ClassNotFoundException e) {
			// Ignore.
		}

		// try load the factory from its class location
		if (null == factory && null != ref.getFactoryClassLocation()) {
			factory = (ObjectFactory) loadFactoryFromLocation(
					ref.getFactoryClassName(), ref.getFactoryClassLocation());
		}
		// if factory cannot be loaded
		if (null == factory) {
			// return o
			return o;
		}

		// get object instance using the factory and return it
		return factory.getObjectInstance(ref, n, c, h);
	}

	private static Object getObjectInstanceByObjectFactory(Object o, Name n,
			Context c, Hashtable<?, ?> h) throws NamingException, Exception {
		// obtain object factories from hashtable and service provider resource
		// file
		final String fnames[] = EnvironmentReader
				.getFactoryNamesFromEnvironmentAndProviderResource(h, c,
						Context.OBJECT_FACTORIES);
		for (final String element : fnames) {
			// new factory instance by its class name
			ObjectFactory factory = null;
			try {
				factory = (ObjectFactory) classForName(element).newInstance();
			} catch (final Exception e) {
				continue;
			}
			// create object using factory
			final Object obj = factory.getObjectInstance(o, n, c, h);
			if (null != obj) {
				return obj;
			}
		}
		// no object factory succeeded, return null
		return null;
	}

	private static Object getObjectInstanceByUrlContextFactory(String url,
			Name n, Context c, Hashtable<?, ?> h, String pkgPrefixes[],
			String schema) throws NamingException {
		// if schema is empty or null, fail, return null
		if (null == schema || 0 == schema.length()) {
			return null;
		}

		for (final String element : pkgPrefixes) {
			ObjectFactory factory = null;
			try {
				// create url context factory instance
				final String clsName = element + "." //$NON-NLS-1$
						+ schema + "." //$NON-NLS-1$
						+ schema + "URLContextFactory"; //$NON-NLS-1$
				factory = (ObjectFactory) classForName(clsName).newInstance();
			} catch (final Exception e) {
				// failed to create factory, continue trying
				continue;
			}
			try {
				// create obj using url context factory
				final Object obj = factory.getObjectInstance(url, n, c, h);
				// if create success, return it
				if (null != obj) {
					return obj;
				}
			} catch (final Exception e) {
				// throw NamingException, if factory fails
				if (e instanceof NamingException) {
					throw (NamingException) e;
				}
				// jndi.21=Failed to create object instance
				final NamingException nex = new NamingException(
						Messages.getString("jndi.21")); //$NON-NLS-1$
				nex.setRootCause(e);
				throw nex;
			}
		}
		// fail to create using url context factory, return null
		return null;
	}

	private static Object getObjectInstanceByUrlRefAddr(Name n, Context c,
			Hashtable<?, ?> h, Reference ref) throws NamingException {
		// obtain pkg prefixes from hashtable and service provider resource file
		final String pkgPrefixes[] = EnvironmentReader
				.getFactoryNamesFromEnvironmentAndProviderResource(h, c,
						Context.URL_PKG_PREFIXES);
		// for each RefAddr
		final Enumeration<RefAddr> enumeration = ref.getAll();
		while (enumeration.hasMoreElements()) {
			final RefAddr addr = enumeration.nextElement();
			// if it is StringRefAddr and type is URL
			if (addr instanceof StringRefAddr
					&& addr.getType().equalsIgnoreCase("URL")) { //$NON-NLS-1$
				// get the url address
				final String url = (String) ((StringRefAddr) addr).getContent();
				// try create using url context factory
				final Object obj = getObjectInstanceByUrlContextFactory(url, n,
						c, h, pkgPrefixes, UrlParser.getScheme(url));
				// if success, return the created obj
				if (null != obj) {
					return obj;
				}
			}
		}
		// failed to create using any StringRefAddr of address type URL, return
		// null
		return null;
	}

	/**
	 * Get the state of an Object.
	 * <p>
	 * The <code>Context.STATE_FACTORIES</code> property from the
	 * <code>Hashtable h</code> together with the
	 * <code>Context.STATE_FACTORIES</code> property from the provider resource
	 * file of the <code>Context c</code> provides the list of factories tried
	 * to get an object's state.
	 * </p>
	 * <p>
	 * Each factory in the list is attempted to be loaded using the context
	 * class loader. Once a class is loaded then it can be used to create a new
	 * instance of it to obtain the factory which can then use its
	 * <code>getStateToBind</code> to find the return object. Once an object is
	 * found then it is not necessary to examine further factories and the
	 * object is returned it as the return parameter.
	 * </p>
	 * <p>
	 * If no factory is loaded or all loaded factories fail to return an object
	 * then return the supplied <code>Object o</code> as the return param.
	 * </p>
	 * <p>
	 * Note for service provider implementors: Classes which implement the
	 * <code>StateFactory</code> interface must be public with a public
	 * constructor that has no parameters.
	 * </p>
	 * 
	 * @param o
	 *            an object which may provide reference or location information.
	 *            May be null.
	 * @param n
	 *            the name of the <code>Object</code> relative to the default
	 *            initial context (or relative to the Context c if it is
	 *            supplied)
	 * @param c
	 *            the <code>Context</code> to which the <code>Name</code> is
	 *            relative
	 * @param h
	 *            a <code>Hashtable</code> containing environment properties and
	 *            values - may be null
	 * @return the state of the specified object
	 * @throws NamingException
	 *             if one is encountered
	 */
	public static Object getStateToBind(Object o, Name n, Context c,
			Hashtable<?, ?> h) throws NamingException {

		// obtain state factories from hashtable and service provider resource
		// file
		final String fnames[] = EnvironmentReader
				.getFactoryNamesFromEnvironmentAndProviderResource(h, c,
						Context.STATE_FACTORIES);

		for (final String element : fnames) {
			// new factory instance by its class name
			StateFactory factory = null;
			try {
				factory = (StateFactory) classForName(element).newInstance();
			} catch (final Exception e) {
				continue;
			}
			// try obtain state using the factory
			final Object state = factory.getStateToBind(o, n, c, h);
			// if a state obtained successfully, return it
			if (null != state) {
				return state;
			}
		}

		// all factories failed, return the input argument o
		return o;
	}

	/**
	 * Creates a URL <code>Context</code> which can subsequently be used to
	 * resolve any URLs with the URL scheme s. A <code>URLContextFactory</code>
	 * is a type of <code>ObjectFactory</code> used to create a
	 * <code>URLContext</code> when <code>getObjectInstance</code> is invoked
	 * with the <code>Object o</code> set to null (see the description of
	 * <code>ObjectFactory</code>).
	 * <p>
	 * This <code>getURLContext</code> method tries to locate the
	 * <code>URLContextFactory</code> based on the
	 * <code>Context.URL_PKG_PREFIXES</code> property which contains the
	 * prefixes to be tried as the start of the package name. (See
	 * <code>Context</code>).
	 * </p>
	 * <p>
	 * Each package prefix entry (and finally the default value) are tried to
	 * find the class which can be used to create the Context.
	 * </p>
	 * <p>
	 * A full class name is derived as
	 * <code>packageprefix.s.sURLContextFactory</code> where <code>s</code> is
	 * the scheme.
	 * </p>
	 * <p>
	 * For example if a scheme is abc and the package prefix to try is com.ibm
	 * then the factory class to try is
	 * <code>com.ibm.abc.abcURLContextFactory</code>. Once a factory is created
	 * then a <code>Context</code> is created using the special use of
	 * <code>ObjectFactory.getObjectInstance</code>.
	 * </p>
	 * <p>
	 * Once a first factory is created, it is used to create the context, and NO
	 * further attempts will be made on other pkg prefixes.
	 * </p>
	 * 
	 * @param schema
	 *            the URL scheme to which the Context will relate
	 * @param envmt
	 *            a <code>Hashtable</code> containing environment properties and
	 *            values - may be null
	 * @return the URL <code>Context</code> or null if no
	 *         <code>URLContextFactory</code> instance can be created and
	 *         therefore a Context cannot be created.
	 * @throws NamingException
	 *             if one is encountered.
	 */
	public static Context getURLContext(String schema, Hashtable<?, ?> envmt)
			throws NamingException {

		if (null == schema || 0 == schema.length()) {
			return null;
		}

		// obtain pkg prefixes from hashtable
		final String pkgPrefixes[] = EnvironmentReader
				.getFactoryNamesFromEnvironmentAndProviderResource(envmt, null,
						Context.URL_PKG_PREFIXES);

		for (final String element : pkgPrefixes) {
			// create factory instance
			ObjectFactory factory;
			try {
				final String clsName = element + "." //$NON-NLS-1$
						+ schema + "." //$NON-NLS-1$
						+ schema + "URLContextFactory"; //$NON-NLS-1$
				factory = (ObjectFactory) classForName(clsName).newInstance();
			} catch (final Exception ex) {
				// fail to create factory, continue to try another
				continue;
			}
			try {
				// create url context using the factory, and return it
				return (Context) factory.getObjectInstance(null, null, null,
						envmt);
			} catch (final NamingException e) {
				// find NamingException, throw it
				throw e;
			} catch (final Exception e) {
				// other exception, throw as NamingException
				// jndi.22=other exception happens: {0}
				final NamingException nex = new NamingException(
						Messages.getString("jndi.22", e.toString())); //$NON-NLS-1$
				nex.setRootCause(e);
				throw nex;
			}
		}

		// cannot create context instance from any pkg prefixes, return null
		return null;
	}

	/**
	 * Returns true when an <code>InitialContextFactoryBuilder</code> has been
	 * installed.
	 * 
	 * @return true when an <code>InitialContextFactoryBuilder</code> has been
	 *         installed.
	 */
	public static boolean hasInitialContextFactoryBuilder() {
		return null != icfb;
	}

	/*
	 * If cannot load class, return null. Throws any exceptions except
	 * ClassNotFoundException
	 */
	private static Object loadFactoryFromLocation(String clsName,
			String location) throws Exception {

		// convert location into an array of URL, separated by ' '
		final StringTokenizer st = new StringTokenizer(location, " "); //$NON-NLS-1$
		final URL urls[] = new URL[st.countTokens()];
		for (int i = 0; i < urls.length; i++) {
			urls[i] = new URL(st.nextToken());
		}

		// new a URLClassLoader from the URLs
		final URLClassLoader l = new URLClassLoader(urls);

		// try load factory by URLClassLoader
		try {
			// return the new instance
			return l.loadClass(clsName).newInstance();
		} catch (final ClassNotFoundException e) {
			// return null if class loading failed
			return null;
		}
	}

	/**
	 * Install an <code>InitialContextFactoryBuilder</code>. Once this has been
	 * set it cannot be reset. Attempts to do so cause an
	 * <code>IllegalStateException</code>. The builder can only be installed if
	 * the security policy allows the setting of the factory.
	 * 
	 * @param icfb
	 *            the builder to be installed - can be null, but then no builder
	 *            is installed.
	 * @throws IllegalStateException
	 *             if an builder has already been installed.
	 * @throws SecurityException
	 *             is a security error prevents the installation.
	 * @throws NamingException
	 *             for other errors encountered.
	 */
	public static void setInitialContextFactoryBuilder(
			InitialContextFactoryBuilder icfb) throws IllegalStateException,
			SecurityException, NamingException {
		// check security access
		final SecurityManager sm = System.getSecurityManager();
		if (null != sm) {
			sm.checkSetFactory();
		}
		synchronized (NamingManager.class) {
			if (null != NamingManager.icfb) {
				// jndi.1E=InitialContextFactoryBuilder cannot be reset
				throw new IllegalStateException(Messages.getString("jndi.1E")); //$NON-NLS-1$
			}
			NamingManager.icfb = icfb;
		}
	}

	/**
	 * Install an <code>ObjectFactoryBuilder</code>. Once this has been set it
	 * cannot be reset. Attempts to do so cause an
	 * <code>IllegalStateException</code>. The builder can only be installed if
	 * the security policy allows the setting of the factory.
	 * 
	 * @param ofb
	 *            the <code>ObjectFactoryBuilder</code> to be installed - can be
	 *            null, but then no builder is installed.
	 * @throws IllegalStateException
	 *             if an <code>ObjectFactoryBuilder</code> has already been
	 *             installed.
	 * @throws SecurityException
	 *             is a security error prevents the installation.
	 * @throws NamingException
	 *             for other errors encountered.
	 */
	public static synchronized void setObjectFactoryBuilder(
			ObjectFactoryBuilder ofb) throws IllegalStateException,
			SecurityException, NamingException {

		if (null != NamingManager.ofb) {
			// jndi.1F=ObjectFactoryBuilder cannot be reset
			throw new IllegalStateException(Messages.getString("jndi.1F")); //$NON-NLS-1$
		}

		// check security access
		final SecurityManager sm = System.getSecurityManager();
		if (null != sm) {
			sm.checkSetFactory();
		}

		NamingManager.ofb = ofb;
	}

	NamingManager() {
		super();
		// package private to prevent it being instanced but make it can be
		// subclassed by DirectoryManager
	}

}
